#include "netmsg.h"

// message type includes:
#include "connectmsg.h"
#include "connectreplymsg.h"
#include "sourcemsg.h"
#include "processupdatemsg.h"
#include "resultsmsg.h"

#include <QDebug>

CNetMsg::CNetMsg(tNetMsgType type)
		: m_nType(type)
{
}


CNetMsg::tNetMsgType CNetMsg::getType() const
{
	return m_nType;
}

quint8 CNetMsg::getProtocolVersion()
{
	return PROTOCOL_VERSION;
}


CNetMsg *CNetMsg::getMsg(QDataStream &s, QString *pErrorStr)
{
	// sanity - make sure the data stream is happy:
	if (s.status() != QDataStream::Ok)
	{
		qDebug("Datastream status is not OK");
		if (pErrorStr != 0)
			*pErrorStr = "Datastream status is not OK";
	return NULL;
	}

	// we need to wait for a total of 6 bytes to arrive. That's 2 bytes for the
	// protocol header, plus 4 bytes length information. Don't wait forever though:
	while (s.device()->bytesAvailable() < 6)
	{
		if (!s.device()->waitForReadyRead(500))
		{
			qDebug("Timed out waiting for 6 byte packet header.");
			if (pErrorStr)
			{
				*pErrorStr = "Timed out waiting for 6 byte packet header.";
			}
			return NULL;
		}
	}

	// how do we do this? simple: we know the the first two bytes will always be
	// PROTOCOL_VERSION and MSG_TYPE. We use the protocl version to make sure that
	// we can accept this message (if it's not the same we just return NULL). We
	// then use the message type to work out what sub-class we should create. After
	// creating the sub class, we simply call it's serialisation method to reconstitute
	// it from the data stream, and pass it back to the user.

	char buff[2];
	if (s.readRawData(buff, 2) != 2)
	{
		qDebug("Could not read 2 byte header block to determine protocol version and message type");
		if (pErrorStr != 0)
		{
			*pErrorStr = "Could not read 2 byte header block to determine protocol version and message type";
		}
	return NULL;
	}

	if (buff[0] != PROTOCOL_VERSION)
	{
		qDebug() << QString("Protocol version does not match: expected %1, got %2 instead").arg(PROTOCOL_VERSION).arg(buff[0]);
		if (pErrorStr != 0)
		{
			*pErrorStr = QString("Protocol version does not match: expected %1, got %2 instead").arg(PROTOCOL_VERSION).arg(buff[0]);
		}
		return NULL;
	}

	// now we can read the four-byte size block:
	quint32 nSize;
	s >> nSize;

	qDebug() << "New Message type:" << buff[1] << "size should be" << nSize << "Bytes long";
	while (s.device()->bytesAvailable() < nSize)
	{
		if (!s.device()->waitForReadyRead(1000))
		{
			qDebug("Timed out waiting for packet data");
			if (pErrorStr)
			{
				*pErrorStr = "Timed out waiting for packet data";
			}
			return NULL;
		}
	}

	QByteArray arr(nSize, '\0');
	s.readRawData(arr.data(), nSize);

	QDataStream tempDS(&arr, QIODevice::ReadOnly);

	qDebug() << "Device reports" << s.device()->bytesAvailable() << "bytes available";


	CNetMsg *pMsg =0;

	switch (buff[1])
	{
	case MSG_TYPE_CONNECT:
	{
		pMsg = new CConnectMsg;
		pMsg->serialiseFromStream(tempDS);
	}
	break;

	case MSG_TYPE_CONNECT_REPLY:
	{
		pMsg = new CConnectReplyMsg;
		pMsg->serialiseFromStream(tempDS);
	}
	break;

	case MSG_TYPE_SOURCE:
	{
		pMsg = new CSourceMsg;
		pMsg->serialiseFromStream(tempDS);
	}
	break;
	case MSG_TYPE_PROC_UPDATE:
	{
		pMsg = new CProcessUpdateMsg;
		pMsg->serialiseFromStream(tempDS);
	}
	break;
	case MSG_TYPE_RESULTS:
	{
		pMsg = new CResultsMsg;
		pMsg->serialiseFromStream(tempDS);
	}
	break;
	default:
	Q_ASSERT_X(0, Q_FUNC_INFO, "Unknown message type encountered!");
	if (pErrorStr != 0)
		*pErrorStr = QString("Unknown message type Id %1").arg(buff[1]);
	return NULL;
	}

	pMsg->m_nType = (tNetMsgType) buff[1];
	return pMsg;
}

bool CNetMsg::setMsg(QDataStream &s, QString *pErrorStr) const
{
	// make sure data stream status is Ok:
	if (s.status() != QDataStream::Ok)
	{
		qDebug("Data stream status is not Ok");
		if (pErrorStr != 0)
		{
			*pErrorStr = "Data stream status is not Ok";
		}
		return false;
	}

	char buff[2];
	buff[0] = PROTOCOL_VERSION;
	buff[1] = getType();

	if (s.writeRawData(buff, 2) != 2)
	{
		qDebug("Could not write 2 byte header to data stream");
		if (pErrorStr != 0)
		{
			*pErrorStr = "Could not write 2 byte header to data stream";
		}
		return false;
	}

	// tell sub-classes to get their act together and serialise themselves to the stream
	// but get them to write to a QByteArray instead of directly to the socket, since we need to
	// know how much data they're going to write:
	QByteArray arr;
	QDataStream tempDS(&arr, QIODevice::WriteOnly);

	serialiseToStream(tempDS);

	// now write the length to the socket:
	s << (quint32) arr.length();
	s.writeRawData(arr.data(), arr.length());

	return true;
}
